/* Copyright 2010 ESRI
* 
* All rights reserved under the copyright laws of the United States
* and applicable international laws, treaties, and conventions.
* 
* You may freely redistribute and use this sample code, with or
* without modification, provided you include the original copyright
* notice and use restrictions.
* 
* See the use restrictions.
* 
*/
package com.helloae;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

import com.esri.arcgis.datasourcesfile.ShapefileWorkspaceFactory;
import com.esri.arcgis.geodatabase.Feature;
import com.esri.arcgis.geodatabase.FeatureClass;
import com.esri.arcgis.geodatabase.FeatureCursor;
import com.esri.arcgis.geodatabase.Field;
import com.esri.arcgis.geodatabase.Fields;
import com.esri.arcgis.geodatabase.GeometryDef;
import com.esri.arcgis.geodatabase.IFeatureClass;
import com.esri.arcgis.geodatabase.Workspace;
import com.esri.arcgis.geodatabase.esriFeatureType;
import com.esri.arcgis.geodatabase.esriFieldType;
import com.esri.arcgis.geometry.IGeographicCoordinateSystem;
import com.esri.arcgis.geometry.Point;
import com.esri.arcgis.geometry.SpatialReferenceEnvironment;
import com.esri.arcgis.geometry.esriGeometryType;
import com.esri.arcgis.geometry.esriSRGeoCSType;
import com.esri.arcgis.system.AoInitialize;
import com.esri.arcgis.system.Cleaner;
import com.esri.arcgis.system.EngineInitializer;
import com.esri.arcgis.system.esriLicenseProductCode;
import com.esri.arcgis.system.esriLicenseStatus;

public class CreateShapefile {

  public CreateShapefile(){
    
  }
  
  /**
   * 本例是ArcGIS官网自带的例子，功能也是由包含xy坐标的txt文件生成shp文件，但是没有使用XYEventSource，因此较复杂，例子地址如下：
   * （http://help.arcgis.com/en/sdk/10.0/java_ao_adf/conceptualhelp/engine/index.html#/Create_a_shapefile_from_a_text_file_with_XY_values/000100000404000000/）
   * Main Method - The console application entry point.
   * 
   * @param args String[] Command line argument
   */
  public static void main(String[] args) {
    System.out.println("开始创建Shapefile ———— 一个AO SDK示例。");
    
    try{
      //初始化engine
      EngineInitializer.initializeEngine();
      
      //初始化ArcGIS许可
      AoInitialize aoInit = new AoInitialize();
      initializeArcGISLicenses(aoInit);
      
      //获得ArcGIS DevelopeKit根目录
      String devKitHome = System.getenv("AGSDEVKITJAVA");
      
      //定义输入、输出文件夹及文件名
      String outPath = getOutputDir() + File.separator + "createshapefilefromtext";
      String outName = "shapefromtext.shp";
      String ecliseWorkspace = System.getenv("Eclipse3.6Workspace");
      String textFilePath = ecliseWorkspace + "HelloAE" + File.separator + "DataSet" 
                       + File.separator + "other" + File.separator + "texttoshape.txt";  
      boolean isFeatureBufferUsed = true;
      
      File shapefileDir = new File(outPath);
      shapefileDir.mkdir();
      
      File outShapefileFile = new File(shapefileDir, outName);
      if (outShapefileFile.exists()) {
        System.out.println("要创建的文件: " + outShapefileFile.getAbsolutePath() + "已存在。");
        System.out.println("请删除文件后（包括.shx和.dbf文件）再重试。程序已退出！");
        System.exit(-1);
      }
      
      File textFile = new File(textFilePath);
      if (!textFile.canRead()) {
        System.out.println("找不到输入的txt文件：" + textFile.getAbsolutePath());
        System.out.println("程序将退出。");
        System.exit(-1);
      }

      CreateShapefile createShapefile = new CreateShapefile();
      FeatureClass featureClass = createShapefile.createShapefile(outPath, outName);
      createShapefile.addPoints(textFilePath, featureClass, isFeatureBufferUsed);
      
      System.out.println("使用" + textFilePath + "成功创建 " + outShapefileFile.getAbsolutePath());
            
      //Ensure any ESRI libraries are unloaded in the correct order
      //卸载arcobjects.jar
      aoInit.shutdown();
      System.out.println("Congratulations!");
    }catch(Exception e){
      System.out.println("Error: " + e.getMessage());
      System.out.println("程序运行失败，即将推出。");
      e.printStackTrace();
      System.exit(-1);
    }
  }

  private static void initializeArcGISLicenses(AoInitialize aoInit) {
    try {
      if (aoInit.isProductCodeAvailable(esriLicenseProductCode.esriLicenseProductCodeEngine) 
          == esriLicenseStatus.esriLicenseAvailable)
        aoInit.initialize(esriLicenseProductCode.esriLicenseProductCodeEngine);
      else if (aoInit.isProductCodeAvailable(esriLicenseProductCode.esriLicenseProductCodeArcView) 
          == esriLicenseStatus.esriLicenseAvailable)
        aoInit.initialize(esriLicenseProductCode.esriLicenseProductCodeArcView);
      else{
        System.err.println("无法初始化Engine或是ArcView许可。程序将退出。");
        System.exit(-1);
      }
    } catch (Exception e) {e.printStackTrace();}
  }

  /**
   * 创建一个shapefile用来放置点要素
   *
   * @param shapefilePath shapefile的存储路径.
   * @param shapefileName shapefile的文件名.
   * @return FeatureClass 放置点要素的FeatureClass.
   * @throws IOException shapefile创建失败时产生异常.
   */
  private FeatureClass createShapefile(String shapefilePath, String shapefileName) throws IOException {
    try {
      String geometryShapeFieldName = "Shape";
      
      //在输出文件夹创建一个workspace
      ShapefileWorkspaceFactory shapefileWorkspaceFactory = new ShapefileWorkspaceFactory();
      Workspace workspace = (Workspace) shapefileWorkspaceFactory.openFromFile(shapefilePath, 0);
        
      //创建一个GeometryDef对象用来存储geometry信息
      GeometryDef geometryDef = new GeometryDef();
      geometryDef.setGeometryType(esriGeometryType.esriGeometryPoint);

      //创建空间参考信息
      SpatialReferenceEnvironment spatialReferenceEnvironment = new SpatialReferenceEnvironment();
      IGeographicCoordinateSystem geographicCoordinateSystem = spatialReferenceEnvironment.createGeographicCoordinateSystem(
          esriSRGeoCSType.esriSRGeoCS_NAD1983);
      
      geometryDef.setSpatialReferenceByRef(geographicCoordinateSystem);

      //将geometry信息存储到geometry shape field中

      Field geometryShapeField = new Field();
      geometryShapeField.setName(geometryShapeFieldName);
      geometryShapeField.setType(esriFieldType.esriFieldTypeGeometry);
      geometryShapeField.setGeometryDefByRef(geometryDef);

      //创建点坐标的属性字段：经度、纬度、value
      //注意：我们并不一定要将经度、纬度创建成属性字段，因为在数据中会存储空间信息。
      //      但是当我们仅仅只查看dbf表格的时候，这样做有时会很有用
      Field lngCoordField = new Field();
      lngCoordField.setLength(30);
      lngCoordField.setName("Longitude");
      lngCoordField.setType(esriFieldType.esriFieldTypeDouble);

      Field latCoordField = new Field();
      latCoordField.setLength(30);
      latCoordField.setName("Latitude");
      latCoordField.setType(esriFieldType.esriFieldTypeDouble);

      Field valCoordField = new Field();
      valCoordField.setLength(30);
      valCoordField.setName("Value");
      valCoordField.setType(esriFieldType.esriFieldTypeDouble);

      //创建一个Fields对象，并将刚才的field都添加进来
      Fields fields = new Fields();
      fields.addField(lngCoordField);
      fields.addField(latCoordField);
      fields.addField(valCoordField);
      fields.addField(geometryShapeField);

      //根据之前的文件名、字段创建一个featureClass
      //创建这个featureClass也会导致shapefile文件的生成
      IFeatureClass temp = workspace.createFeatureClass(shapefileName, fields, null, null,
              esriFeatureType.esriFTSimple, geometryShapeFieldName, ""); 
      
//      FeatureClass featureClass = new FeatureClass(workspace.createFeatureClass(shapefileName, fields, null, null,
//          esriFeatureType.esriFTSimple, geometryShapeFieldName, ""));
      FeatureClass featureClass = new FeatureClass(temp);

      //返回创建好的featureClass，用于添加点

      return featureClass;
    }catch (IOException e) {
      System.out.println("不能用给定的文件名“" + shapefileName + "”创建shapefile文件。");
      throw e;
    }
  }

  /**
   * 将points从txt文件添加到shapefile文件中
   *
   * @param textFilePath 包含点坐标的txt文件路径。
   * @param featureClass 承载点信息的featureClass类。
   * @param useFeatureBuffer whether to add points to a feature buffer or to individual features.
   * @throws IOException if cannot add points to the feature class.
   */
  private void addPoints(String textFilePath, FeatureClass featureClass, boolean useFeatureBuffer) throws IOException {
    System.out.println("创建点要素中...");
    try {
      if (useFeatureBuffer) {
        // Get an insert cursor and a feature buffer for the feature class, so we can add features
        // in a buffered manner.
        FeatureCursor featureCursor = new FeatureCursor(featureClass.IFeatureClass_insert(true));
        Feature featureBuffer = (Feature) featureClass.createFeatureBuffer();

        // Get the column indicies for the coordinate fields to be used
        // when setting values fort these fields.
        Fields fields = (Fields) featureBuffer.getFields();
        int lngIndex = fields.findField("Longitude");
        int latIndex = fields.findField("Latitude");
        int valIndex = fields.findField("Value");

        // Read in a line at a time, parsing the coordinate values for each point
        BufferedReader bufferedReader = new BufferedReader(new FileReader(textFilePath));
        int featureCount = 0;
        for (String textLine = bufferedReader.readLine(); textLine != null; textLine = bufferedReader.readLine(), featureCount++) {
          String[] tokens = textLine.split(", ");
          double lng = Double.parseDouble(tokens[0]);
          double lat = Double.parseDouble(tokens[1]);
          double val = Double.parseDouble(tokens[2]);

          System.out.println("Longitude: " + lng + " Latitude: " + lat + " Value: " + val);

          // Create a new point and set its X,Y coordinates from the longitude,latitude values.
          Point point = new Point();
          point.setX(lng);
          point.setY(lat);

          // Add the point's geometry to the feature buffer's shape field.
          featureBuffer.setShapeByRef(point);

          // Add longitude, latitude, and value values to their individual fields
          featureBuffer.setValue(lngIndex, new Double(lng));
          featureBuffer.setValue(latIndex, new Double(lat));
          featureBuffer.setValue(valIndex, new Double(val));

          // Add the feature at the current/cursor location in the shapefile
          featureCursor.insertFeature(featureBuffer);

          // Flush the insert cursor every 100 features
          if ((featureCount+1) % 100 == 0) {
            System.out.println("Flushing...");
            featureCursor.flush();
          }
        }

        // Flush the insert cursor at the end of the file
        featureCursor.flush();
        Cleaner.release(featureCursor);
      }else {
        //Use createFeature() and store()

        //Get the column indicies for the coordinate fields to be used
        //when setting values fort these fields.
        Fields fields = (Fields) featureClass.getFields();
        int lngIndex = fields.findField("Longitude");
        int latIndex = fields.findField("Latitude");
        int valIndex = fields.findField("Value");

        //Read in a line at a time, parsing the coordinate values for each point
        BufferedReader bufferedReader = new BufferedReader(new FileReader(textFilePath));
        for (String textLine = bufferedReader.readLine(); textLine != null; textLine = bufferedReader.readLine()) {
          String[] tokens = textLine.split(", ");
          double lng = Double.parseDouble(tokens[0]);
          double lat = Double.parseDouble(tokens[1]);
          double val = Double.parseDouble(tokens[2]);

          System.out.println("Longitude: " + lng + " Latitude: " + lat + " Value: " + val);

          //根据经纬度值创建点对象
          Point point = new Point();
          point.setX(lng);
          point.setY(lat);

          //Create a new feature and add the point's geometry to it
          Feature feature = (Feature) featureClass.createFeature();
          feature.setShapeByRef(point);

          //Add longitude, latitude, and value values to their individual fields
          feature.setValue(lngIndex, new Double(lng));
          feature.setValue(latIndex, new Double(lat));
          feature.setValue(valIndex, new Double(val));

          //Persist changes by calling store()
          feature.store();
        }
      }
    }catch (IOException e) {
      System.out.println("无法将点要素添加到shapefile中。");
      throw e;
    }
  }

  /**
   * Convenience method to generate an output directory based on the operating
   * system that the sample is being executed on. 
   * 
   * @return A path to the new directory is return
   */
  private static String getOutputDir() {
    String userDir;
    
    //Get the operating systems user profile or home location depending
    //on which operating system this sample is executed on.
    if(System.getProperty("os.name").toLowerCase().indexOf("win") > -1){
      userDir = System.getenv("UserProfile");
    }else{
      userDir = System.getenv("HOME");
    }
      
    String outputDir = userDir + File.separator + "arcgis_sample_output";
    
    System.out.println("正在创建输出文件夹： " + outputDir);
    
    new File(outputDir).mkdir();
    
    return outputDir;
  }
}